package xiaojian.toolkit.netty.handler.codec.http;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.ReferenceCountUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xiaojian.toolkit.ebus.EventBus;
import xiaojian.toolkit.netty.handler.codec.http.reactor.HttpReactor;
import xiaojian.toolkit.netty.handler.codec.http.request.HttpRequestExt;
import xiaojian.toolkit.netty.transport.TransportUtils;
import xiaojian.toolkit.netty.transport.endpoint.Endpoint;
import xiaojian.toolkit.netty.transport.endpoint.HttpEndpoint;

import java.io.IOException;

@ChannelHandler.Sharable
public abstract class HttpRequestHandler extends ChannelInboundHandlerAdapter {
	private static final Logger logger = LoggerFactory.getLogger(HttpRequestHandler.class);

	private String eventHttpRequestArrived;
	private String eventHttpSessionOpened;
	private String eventHttpSessionClosed;

	private EventBus eventBus;
	private HttpReactor reactor;

	private boolean useSsl;

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		if (!(cause instanceof IOException)) {
			logger.error("exceptionCaught: ", cause);
			ctx.channel().close();
		}
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (!(msg instanceof FullHttpRequest)) {
			logger.warn("received msg {} is not FullHttpRequest", msg);
			return;
		}
		HttpRequestExt req = new HttpRequestExt((FullHttpRequest) msg);

		// -- 单独处理options。
		if (req.getMethod().equals(HttpMethod.OPTIONS)) {
			FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
			response.headers().add("Content-Type", " text/html");
			response.headers().add("Connection", "keep-alive");
			response.headers().add("Content-Length", "2");
			response.headers().add("Access-Control-Allow-Headers", "V,uuid,Msg,Content-Type,Used-Base64");
			response.headers().add("Access-Control-Allow-Origin", "*");
			response.headers().add("Access-Control-Allow-Methods", "OPTIONS,POST,GET");

			response.content().writeBytes("OK".getBytes());

			ctx.channel().writeAndFlush(response);
			ReferenceCountUtil.release(msg);
			return;
		}

		// -- 添加endpoint
		Endpoint endpoint = TransportUtils.getEndpointOfSession(ctx);
		if (null != endpoint) {
			endpoint.messageReceived(ctx, req);
		}

		if (null != eventHttpRequestArrived) {
			eventBus.fireEvent(eventHttpRequestArrived, msg);
		}

		if (null != reactor) {
			reactor.onHttpRequest(req);
		}

		// -- TODO(?)
		ReferenceCountUtil.release(msg);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		if (null != eventHttpSessionClosed) {
			eventBus.fireEvent(eventHttpSessionClosed, ctx.channel());
		}

		Endpoint endpoint = TransportUtils.getEndpointOfSession(ctx);
		if (null != endpoint) {
			endpoint.stop();
		}
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {

		if (useSsl) {
			SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
			sslHandler.handshakeFuture();
		}

		if (null != eventHttpSessionOpened) {
			eventBus.fireEvent(eventHttpSessionOpened, ctx.channel());
		}

		HttpEndpoint httpEndpoint = createEndpoint();
		httpEndpoint.setChannel(ctx.channel());
		httpEndpoint.start();

		TransportUtils.attachEndpointToSession(ctx, httpEndpoint);
	}

	public String getEventHttpRequestArrived() {
		return eventHttpRequestArrived;
	}

	public void setEventHttpRequestArrived(String eventHttpRequestArrived) {
		this.eventHttpRequestArrived = eventHttpRequestArrived;
	}

	public String getEventHttpSessionOpened() {
		return eventHttpSessionOpened;
	}

	public void setEventHttpSessionOpened(String eventHttpSessionOpened) {
		this.eventHttpSessionOpened = eventHttpSessionOpened;
	}

	public String getEventHttpSessionClosed() {
		return eventHttpSessionClosed;
	}

	public void setEventHttpSessionClosed(String eventHttpSessionClosed) {
		this.eventHttpSessionClosed = eventHttpSessionClosed;
	}

	public void setEventBus(EventBus eventBus) {
		this.eventBus = eventBus;
	}

	public void setReactor(HttpReactor reactor) {
		this.reactor = reactor;
	}

	/**
	 * @return the useSsl
	 */
	public boolean isUseSsl() {
		return useSsl;
	}

	/**
	 * @param useSsl
	 *            the useSsl to set
	 */
	public void setUseSsl(boolean useSsl) {
		this.useSsl = useSsl;
	}

	public abstract HttpEndpoint createEndpoint();
}
